package com.qining618.cms.ucenter.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.qining618.cms.ucenter.domain.Menu;
import com.qining618.cms.ucenter.enums.MenuTypeEnum;
import com.qining618.cms.ucenter.mapper.MenuMapper;
import com.qining618.cms.ucenter.service.MenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qining618.cms.ucenter.service.RoleMenuService;
import com.qining618.common.exception.BusinessException;
import com.qining618.common.result.ErrorCode;
import com.qining618.common.utils.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 * 菜单 服务实现类
 * </p>
 *
 * @author qining
 * @date 2019-09-16
 */
@Transactional(readOnly = true, rollbackFor = Exception.class)
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    @Autowired
    private RoleMenuService roleMenuService;

    @Override
    public IPage<Menu> page(Map<String, Object> params) {
        return this.baseMapper.page(Query.of(params), params);
    }

    @Override
    public List<Menu> tree() {
        List<Menu> menuList = this.list();
        List<Menu> treeList = new ArrayList<>();
        for (Menu menu : menuList) {
            if (menu.getParentId() == 0) {
                menu.setChildren(new ArrayList<>());
                treeList.add(menu);
                continue;
            }
            for (Menu tree : treeList) {
                if (Objects.equals(menu.getParentId(), tree.getId())) {
                    tree.getChildren().add(menu);
                    break;
                }
            }
        }
        return treeList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Menu add(String code, String name, MenuTypeEnum type, Long parentId) {
        // 上级菜单是否存在
        if (parentId != 0) {
            Menu parentMenu = this.baseMapper.selectById(parentId);
            if (parentMenu == null || MenuTypeEnum.MENU != parentMenu.getType()) {
                throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "上级菜单不存在");
            }
        }
        // 菜单编号是否存在
        boolean isExist = this.baseMapper.existByCode(code);
        if (isExist) {
            throw new BusinessException(ErrorCode.DATA_CONFLICT, "菜单已存在");
        }

        // 保存菜单信息
        Menu menu = new Menu();
        menu.setParentId(parentId).setCode(code).setName(name).setType(type);
        int opNum = this.baseMapper.insert(menu);
        if (opNum != 1) {
            throw new BusinessException(ErrorCode.DB_ERROR);
        }
        return menu;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Menu edit(Long id, String code, String name, MenuTypeEnum type, Long parentId) {
        // 菜单检验
        Menu menu = this.baseMapper.selectById(id);
        if (menu == null) {
            throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "菜单不存在");
        }

        // 上级菜单发生变化 检验是否存在
        if (!Objects.equals(menu.getParentId(), parentId)) {
            Menu parentMenu = this.baseMapper.selectById(parentId);
            if (parentMenu == null || MenuTypeEnum.MENU != parentMenu.getType()) {
                throw new BusinessException(ErrorCode.DATA_NOT_FOUND, "上级菜单不存在");
            }
        }

        // 菜单编号发生变化 检验是否已存在
        if (!Objects.equals(code, menu.getCode())) {
            boolean isExist = this.baseMapper.existByCode(code);
            if (isExist) {
                throw new BusinessException(ErrorCode.DATA_CONFLICT, "菜单已存在");
            }
        }

        // 保存菜单信息
        menu.setParentId(parentId).setCode(code).setName(name).setType(type);
        int opNum = this.baseMapper.updateById(menu);
        if (opNum != 1) {
            throw new BusinessException(ErrorCode.DB_ERROR);
        }
        return menu;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteById(Long id) {
        // 获取下级菜单ids
        List<Long> childIds = this.getAllChildIds(id);

        // 菜单id
        childIds.add(id);

        // 删除角色菜单关联信息
        childIds.forEach(menuId -> this.roleMenuService.deleteByMenuId(menuId));

        // 删除所有菜单及下级菜单
        this.baseMapper.deleteBatchIds(childIds);
    }

    /**
     * 获取指定上级的所有下级菜单id
     * @param parentId 上级id
     * @return List&lt;Long&gt;
     */
    private List<Long> getAllChildIds(Long parentId) {
        List<Long> childIds = this.baseMapper.getChildIds(parentId);
        for (Long childId : childIds) {
            childIds.addAll(this.getAllChildIds(childId));
        }
        return childIds;
    }
}
